package com.atguigu.gmall.order.biz.impl;

import com.atguigu.gmall.cart.entity.CartInfo;
import com.atguigu.gmall.common.config.mq.MqService;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.enums.OrderStatus;
import com.atguigu.gmall.enums.PaymentWay;
import com.atguigu.gmall.enums.ProcessStatus;
import com.atguigu.gmall.feign.cart.CartFeignClient;
import com.atguigu.gmall.feign.product.ProductSkuDetailFeignClient;
import com.atguigu.gmall.feign.user.UserFeignClient;
import com.atguigu.gmall.feign.ware.WareFeignClient;
import com.atguigu.gmall.order.biz.OrderBizService;
import com.atguigu.gmall.order.entity.OrderDetail;
import com.atguigu.gmall.order.entity.OrderInfo;
import com.atguigu.gmall.order.ov.OrderConfirmRespVo;
import com.atguigu.gmall.order.ov.OrderSubmitVo;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.user.entity.UserAddress;
import com.atguigu.gmall.utils.UserAuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderBizServiceImpl implements OrderBizService {

    @Autowired
    CartFeignClient cartFeignClient;

    @Autowired
    ProductSkuDetailFeignClient productSkuDetailFeignClient;

    @Autowired
    MqService mqService;
    @Autowired
    UserFeignClient userFeignClient;

    @Autowired
    WareFeignClient wareFeignClient;

    @Autowired
    OrderInfoService orderInfoService;

    @Autowired
    OrderDetailService orderDetailService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Override
    public OrderConfirmRespVo getConfirmData() {
        OrderConfirmRespVo orderConfirmRespVo = new OrderConfirmRespVo();


        //1. 商品列表
        // 远程找购物车要
        List<CartInfo> cartInfos = cartFeignClient.getChecked().getData();
        List<OrderConfirmRespVo.SkuDetail> skuDetailList = cartInfos.stream()
                .map(item -> {
                    OrderConfirmRespVo.SkuDetail skuDetail = new OrderConfirmRespVo.SkuDetail();
                    skuDetail.setSkuId(item.getSkuId());
                    skuDetail.setSkuNum(item.getSkuNum());
                    skuDetail.setImgUrl(item.getImgUrl());
                    skuDetail.setSkuName(item.getSkuName());
                    // 实时查询价格
                    BigDecimal data = productSkuDetailFeignClient.getPrice(skuDetail.getSkuId()).getData();
                    skuDetail.setOrderPrice(data);

                    // 查询是否有货
                    String stock = wareFeignClient.hasStock(skuDetail.getSkuId(), skuDetail.getSkuNum());
                    skuDetail.setHasStock(stock);
                    return skuDetail;
                })
                .collect(Collectors.toList());
        orderConfirmRespVo.setDetailArrayList(skuDetailList);

        //总数量
        Integer totalNum = skuDetailList.stream()
                .map(item -> item.getSkuNum())
                .reduce((o1, o2) -> o1 + o2)
                        .get();
        orderConfirmRespVo.setTotalNum(totalNum);

        //总金额
        BigDecimal totalAmount = skuDetailList.stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        orderConfirmRespVo.setTotalAmount(totalAmount);

        Long userId = UserAuthUtils.getUserId();
        List<UserAddress> addresses = userFeignClient.getUserAddress(userId).getData();
        orderConfirmRespVo.setUserAddressList(addresses);

        //随机生成流水号
        // 防止重新提交订单
        String uuid = "ATGUIGU-"+System.currentTimeMillis()+"-"+userId;
        //给客户端放一个流水号
        orderConfirmRespVo.setTradeNo(uuid);

        //给Redis放一个流水号
        redisTemplate.opsForValue().set(RedisConst.REPEAT_TOKEN+uuid,"1",5, TimeUnit.MINUTES);


        return orderConfirmRespVo;
    }

    @Override
    public Long submitOrder(OrderSubmitVo submitVo, String tradeNo) {
        //校验：后端永远不要相信前端发送来的数据！！，能校验就校验。

        //参数校验：交给Jsr303校验功能来做。

        //业务校验
        //  --令牌校验：
        Boolean delete = redisTemplate.delete(RedisConst.REPEAT_TOKEN + tradeNo);
        if (!delete){
            throw new GmallException(ResultCodeEnum.REPEAT_REQUEST);
        }
        //删除成功
        //  --校验库存：
        List<OrderSubmitVo.OrderDetailListDTO> noStockSku = submitVo.getOrderDetailList()
                .stream()
                .filter(item -> "0".equals(wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum())))
                .collect(Collectors.toList());
        if (noStockSku.size() > 0 && noStockSku != null){
            String skuNames = noStockSku.stream()
                    .map(OrderSubmitVo.OrderDetailListDTO::getSkuName)
                    .reduce((o1, o2) -> o1 + ";" + o2).get();
            GmallException gmallException = new GmallException(skuNames + "没有库存了",ResultCodeEnum.NO_STOCK.getCode());
            throw gmallException;
        }
        //  --校验价格：
        List<OrderSubmitVo.OrderDetailListDTO> priceChangeSkus = submitVo.getOrderDetailList().stream()
                .filter(item -> {
                    BigDecimal price = item.getOrderPrice();
                    BigDecimal data = productSkuDetailFeignClient.getPrice(item.getSkuId()).getData();
                    return Math.abs(price.subtract(data).doubleValue()) >= 0.0001;
                }).collect(Collectors.toList());
        if (priceChangeSkus.size()>0 && priceChangeSkus != null){
            String priceChangeSkusName = priceChangeSkus.stream()
                    .map(OrderSubmitVo.OrderDetailListDTO::getSkuName)
                    .reduce((o1, o2) -> o1 + ";" + o2)
                    .get();
            throw new GmallException(priceChangeSkusName+"价格变化，请重新确认",ResultCodeEnum.PRICE_CHANGE.getCode());
        }

        //超卖

        //1、给OrderInfo保存订单基本信息
        OrderInfo orderInfo = prepareOrderInfo(submitVo,tradeNo);
        // >1.雪花算法生成的订单Id
        orderInfoService.save(orderInfo);
        Long orderInfoId = orderInfo.getId();

        //30min以后关闭
       /* ScheduledExecutorService pool = Executors.newScheduledThreadPool(4);
        pool.schedule(()->{
            closeOrder(orderInfoId);
        },30,TimeUnit.MINUTES);
*/
        //2、给OderDetail保存订单详细信息
        List<OrderDetail> details = prepareOderDetails(submitVo,orderInfoId);
        orderDetailService.saveBatch(details);
        //3、发送订单创建消息
        mqService.send(orderInfo, MqConst.ORDER_EVENT_EXCHANGE,MqConst.ORDER_CREATE_RK);
        //4、TODO：删除购物车中选中的商品
//        cartFeignClient.deleteChecked();
        return orderInfoId;
    }

    @Override
    public boolean closeOrder(Long id, Long userId) {

        ProcessStatus closed = ProcessStatus.CLOSED;
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, closed.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus, closed.name())
                .eq(OrderInfo::getId, id)
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID)
                .eq(OrderInfo::getProcessStatus, ProcessStatus.UNPAID)
                .update();
        log.info("订单：{}，关闭：{}",id,update);
        return update;
    }

    @Override
    public void payedOrder(String out_trade_no, Long userId) {
        //关单消息和支付消息如果同事抵达，无论谁先抵达，最终消息都是以支付状态为准。
        //1.关单先运行。改成已关闭，支付后运行就应该改回来已支付。
        //2.支付先运行，改为已支付，关单后运行什么都不错。

        //订单时未支付或者是已关闭，都可以改为已支付。
        //update OrderStatus = 已支付 and ProcessStatus = 已支付
        //where out_trade_no=? and user_id =  ? and ProcessStatus（未支付，已关闭）
        ProcessStatus payed = ProcessStatus.PAID;

        //修改订单为已支付
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, payed.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus, payed.name())
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOutTradeNo, out_trade_no)
                .in(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name(), OrderStatus.CLOSED.name())
                .in(OrderInfo::getProcessStatus, ProcessStatus.UNPAID.name(), ProcessStatus.CLOSED.name())
                .update();
        log.info("修改{}订单，已支付状态:{}",out_trade_no,update);
    }

    /**
     * 准备OrderDetail数据
     * @param submitVo
     * @return
     */
    private List<OrderDetail> prepareOderDetails(OrderSubmitVo submitVo, Long orderInfoId) {

        List<OrderDetail> orderDetailList = submitVo.getOrderDetailList()
                .stream()
                .map(item -> {
                    OrderDetail orderDetail = new OrderDetail();
                    orderDetail.setOrderId(orderInfoId);
                    orderDetail.setUserId(UserAuthUtils.getUserId());
                    orderDetail.setSkuId(item.getSkuId());
                    orderDetail.setSkuName(item.getSkuName());
                    orderDetail.setImgUrl(item.getImgUrl());
                    orderDetail.setOrderPrice(item.getOrderPrice());
                    orderDetail.setSkuNum(item.getSkuNum());
                    orderDetail.setCreateTime(new Date());
                    orderDetail.setSplitActivityAmount(new BigDecimal(0));
                    orderDetail.setOrderPrice(item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())));
                    orderDetail.setSplitCouponAmount(new BigDecimal(0));
                    return orderDetail;
                }).collect(Collectors.toList());
        return orderDetailList;
    }

    /**
     * 根据前端的Vo数据得到前端的OrderInfo数据
     * @param submitVo
     * @param tradeNo
     * @return
     */
    private OrderInfo prepareOrderInfo(OrderSubmitVo submitVo, String tradeNo) {
        OrderInfo orderInfo = new OrderInfo();


        orderInfo.setConsignee(submitVo.getConsignee());
        orderInfo.setConsigneeTel(submitVo.getConsigneeTel());
        orderInfo.setDeliveryAddress(submitVo.getDeliveryAddress());
        orderInfo.setOrderComment(submitVo.getOrderComment());

        // 订单总金额。
        BigDecimal totalAmount = submitVo.getOrderDetailList()
                .stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        orderInfo.setTotalAmount(totalAmount);

        // 订单状态
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());

        orderInfo.setUserId(UserAuthUtils.getUserId());

        //支付方式
        orderInfo.setPaymentWay(PaymentWay.ONLINE.getComment());

        //对外流水号
        orderInfo.setOutTradeNo(tradeNo);

        //交易体
        String skuName = submitVo.getOrderDetailList().get(0).getSkuName();
        orderInfo.setTradeBody(skuName);
        //创建时间
        orderInfo.setCreateTime(new Date());

        //失效时间 30Min
        Date date = new Date(System.currentTimeMillis() + 30 * 60 * 1000);
        orderInfo.setExpireTime(date);

        //处理状态,比订细致
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());

        //物流单号
        orderInfo.setTrackingNo("");
        //父订单Id
        orderInfo.setParentOrderId(null);

        //订单图片
        String imgUrl = submitVo.getOrderDetailList().get(0).getImgUrl();
        orderInfo.setImgUrl(imgUrl);
        orderInfo.setProvinceId(0L);
        orderInfo.setOperateTime(new Date());


        orderInfo.setActivityReduceAmount(new BigDecimal(0));
        orderInfo.setCouponAmount(new BigDecimal(0));

        // 原金额
        orderInfo.setOriginalTotalAmount(totalAmount);
        orderInfo.setFeightFee(new BigDecimal(0));
        return orderInfo;
    }
//
//    @PostMapping("/xxxxx")
//    public  String submitOrder(String tradeNo){
//        //1.利用占坑方式（分布式锁不带解锁机制）
//        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(tradeNo, "1");
//        if (aBoolean){
//            //占坑成功处理业务
//
//        }else {
//            //提示，请求过于频繁
//
//        }
//
//        ///2. 利用删除
//        Boolean delete = redisTemplate.delete(tradeNo);
//        if (delete){
//            //删除成功执行
//        }else {
//            //删除失败的
//        }
//
//        // 3. 计数机制
//
//    }
}
